package com.smedi.ismedi.personnel.core.application.service.impl;

import cn.hutool.core.util.IdUtil;
import com.alibaba.fastjson.JSONObject;
import com.github.tobato.fastdfs.domain.fdfs.StorePath;
import com.smedi.ismedi.authority.starter.util.VerifyHelper;
import com.smedi.ismedi.personnel.core.application.service.ProfileImageApplicationService;
import com.smedi.ismedi.personnel.core.domain.aggregatemodel.ProfileImage;
import com.smedi.ismedi.personnel.core.domain.repository.ProfileImageTemporaryRepository;
import com.smedi.ismedi.personnel.core.domain.service.PersonDomainService;
import com.smedi.ismedi.personnel.core.domain.service.ProfileImageDomainService;
import com.smedi.ismedi.personnel.core.infrastructure.constant.ConstantField;
import com.smedi.ismedi.personnel.core.infrastructure.constant.CustomError;
import com.smedi.ismedi.personnel.core.infrastructure.enumerate.CapacityEnum;
import com.smedi.ismedi.personnel.core.port.adapter.service.FdfsClientService;
import com.smedi.ismedi.personnel.core.port.adapter.service.ImisRemoteApiService;
import com.smedi.ismedi.web.starter.config.AsyncThreadPoolConfig;
import com.smedi.ismedi.web.starter.exception.LogicException;
import com.smedi.ismedi.web.starter.util.BaseUtil;
import io.netty.util.internal.StringUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.Map;

/**
 * @Author: xulinjun
 * @Date: 2021-6-9 11:49
 */
@Slf4j
@Service
@RequiredArgsConstructor
class ProfileImageApplicationServiceImpl implements ProfileImageApplicationService {
    private final FdfsClientService fdfsClientService;

    private final ProfileImageDomainService profileImageDomainService;

    private final ImisRemoteApiService imisRemoteApiService;

    private final ProfileImageTemporaryRepository profileImageTemporaryRepository;

    private final BaseUtil baseUtil;

    private final PersonDomainService personDomainService;

    private final VerifyHelper verifyHelper;

    @Value("${fdfs.service.address.out}")
    private String FDFS_SERVICE_ADDRESS_OUT;

    /**
     * 校验图片格式
     *
     * @param file
     */
    private void validateProfileImage(MultipartFile file) {
        if (!profileImageDomainService.validateImageSize(file.getSize(), ConstantField.maxProfileImageFileSize, CapacityEnum.MB)) {
            throw LogicException.raise(CustomError.PROFILE_IMAGE_SIZE_ERROR);
        }
        if (!profileImageDomainService.isImage(file)) {
            throw LogicException.raise(CustomError.UPLOAD_FILE_IS_NOT_IMAGE);
        }
    }

    @Override
    public String uploadProfileImage(MultipartFile file) {
        validateProfileImage(file); // 判断文件大小与格式

        // 调用客户端上传头像照片
        try {
            StorePath storePath = fdfsClientService.uploadFileStream(file.getInputStream(), file.getOriginalFilename());
            String imageUUID = IdUtil.simpleUUID(); // 生成唯一的uuid

            // 将对象存储到Redis，用于监控，防止有过多作废的照片信息
            String key = ConstantField.profileImageTempPath + imageUUID + ConstantField.split + storePath.getGroup() + ConstantField.split + storePath.getPath();
            profileImageTemporaryRepository.doInsertTempPath(key, JSONObject.toJSONString(storePath), ConstantField.profileImageTempLive);

            log.debug("Profile Image storePath >>> {}, imageUUID >>> {}", storePath, imageUUID);
            return imageUUID;
        } catch (Exception e) {
            throw LogicException.raise(CustomError.FDFS_UPLOAD_ERROR, e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void uploadProfileImageAndSave(MultipartFile file, Long personId) {
        if (personDomainService.isAvailable(personId)) {
            validateProfileImage(file); // 判断文件大小与格式

            try {
                StorePath storePath = fdfsClientService.uploadFileStream(file.getInputStream(), file.getOriginalFilename());
                profileImageDomainService.saveProfileImage(storePath, personId);
            } catch (Exception e) {
                throw LogicException.raise(CustomError.FDFS_UPLOAD_ERROR, e);
            }
        } else {
            throw LogicException.raise(CustomError.PERSON_NOT_EXIST);
        }
    }

    @Override
    public void dropTempProfileImage(String group, String path) {
        fdfsClientService.deleteFile(group, path);
    }

    private StorePath getProfileImageByRemoteApi(Long referencePersonId) {
        String token = imisRemoteApiService.callToken();
        String base64 = imisRemoteApiService.callImageBase64(token, referencePersonId); // 获取到base64格式图片
        if (!StringUtil.isNullOrEmpty(base64)) {
            File tempFile = baseUtil.base64ToTempFile(base64, ".jpg");
            try {
                StorePath storePath = fdfsClientService.uploadFile(tempFile);
                return storePath;
            } catch (Exception e) {
                throw LogicException.raise(CustomError.FDFS_UPLOAD_ERROR, e);
            } finally {
                System.gc();
                tempFile.delete();
            }
        } else {
            throw LogicException.raise(CustomError.PROFILE_IMAGE_BASE64_EMPTY);
        }
    }

    @Async(AsyncThreadPoolConfig.asyncThreadName)
    @Override
    public void copyProfileImageByRemoteApi(Map<Long, Long> personMap) {
        log.info("开始异步抽取照片信息 >>> {}", personMap);
        for (Map.Entry<Long, Long> entry : personMap.entrySet()) {
            Long referencePersonId = entry.getKey();
            Long personId = entry.getValue();

            try {
                // 抽取图片
                StorePath storePath = getProfileImageByRemoteApi(referencePersonId);
                // 保存信息
                profileImageDomainService.saveProfileImage(storePath, personId);
            } catch (Exception e) {
                log.error("从iMIS端抽取照片信息保存失败 >>> {}，referencePersonId >>> {}, personId >>> {}", e, referencePersonId, personId);
            }
        }
    }

    @Override
    public ProfileImage downloadProfileImage(String imageUUID) {
        ProfileImage profileImage = profileImageDomainService.getProfileImage(imageUUID);
        if (verifyHelper.isAvailable(profileImage)) {
            profileImage.setUrl(FDFS_SERVICE_ADDRESS_OUT + profileImage.getFileGroup() + "/" + profileImage.getFilePath());
        }
        return profileImage;
    }
}
